#include <zephyr.h>
#include <shell/shell.h>
#include <shell/shell.h>

#define USER_WQ_STACK_SIZE 4096
#define USER_WQ_PRIORITY 5

#define MAX_WORK	5

struct device_info {
	union{
		struct k_work work;
		struct k_work_delayable delay_work;
	};

    char name[16];
	struct shell *shell;
};



K_THREAD_STACK_DEFINE(user_wq_stack_area, USER_WQ_STACK_SIZE);

static struct k_work_q user_work_q;
static struct device_info user_device;
static struct device_info system_device;
static struct device_info user_lw_device[MAX_WORK];

static struct device_info delay_work;

void delay_work_handler(struct k_work *item)
{
    struct device_info *the_device =
        CONTAINER_OF(item, struct device_info, work);
    shell_print(the_device->shell, "delay do device %s work", the_device->name);
}



void delay_longwork_handler(struct k_work *item)
{
    struct device_info *the_device =
        CONTAINER_OF(item, struct device_info, work);
    shell_print(the_device->shell, "delay do device %s work start", the_device->name);
	k_msleep(2000);
	shell_print(the_device->shell, "delay do device %s work end", the_device->name);
}

void user_work_handler(struct k_work *item)
{
    struct device_info *the_device =
        CONTAINER_OF(item, struct device_info, work);
    shell_print(the_device->shell, "delay work do device %s work", the_device->name);
}

void user_longwork_handler(struct k_work *item)
{
    struct device_info *the_device =
        CONTAINER_OF(item, struct device_info, work);
    shell_print(the_device->shell, "wq do device %s work start", the_device->name);
	k_msleep(1000);
	shell_print(the_device->shell, "wq do device %s work end", the_device->name);
}

static int workq_user_workq(const struct shell *shell, size_t argc, char **argv)
{
	int ret = 0;
	/* 初始化一个workq */
	k_work_queue_init(&user_work_q);

	/* 启动workq */
	k_work_queue_start(&user_work_q, user_wq_stack_area,
					K_THREAD_STACK_SIZEOF(user_wq_stack_area), USER_WQ_PRIORITY,
					NULL);

	/* 初始化一个work，执行函数为user_work_handler */
	k_work_init(&user_device.work, user_work_handler);
	for(int i =0; i<MAX_WORK; i++){
		sprintf(user_device.name, "U%d", i);
		user_device.shell = shell;
		shell_print(shell, "wq send device %s", user_device.name);

		/* 发送work到workq，由workq执行work的函数 */
		ret = k_work_submit_to_queue(&user_work_q, &user_device.work);
		shell_print(shell, "submit user_device %d ret %d", i, ret);
		uint32_t start = k_uptime_get_32();
		k_msleep(300);
		shell_print(shell, "msleep %d\n", k_uptime_get_32() - start);
	}

	/* 初始化work，用于耗时测试，执行函数为user_longwork_handler */
	for(int i = 0; i<MAX_WORK; i++){
		k_work_init(&user_lw_device[i].work, user_longwork_handler);
		sprintf(user_lw_device[i].name, "LW%d", i);
		user_lw_device[i].shell = shell;
		shell_print(shell, "wq send device %s", user_lw_device[i].name);
		ret = k_work_submit_to_queue(&user_work_q, &user_lw_device[i]);
		shell_print(shell, "submit user_lw_device[%d] ret %d", i, ret);
	}

	k_msleep(2500);

	/* 取消一个未执行的work */
	ret = k_work_cancel(&user_lw_device[MAX_WORK-1].work);
	shell_print(shell, "Cannel work %s", user_lw_device[MAX_WORK-1].name);

	/* 检查work的状态 */
	for(int i = 0; i<MAX_WORK; i++){
		shell_print(shell, "%s busy[0x%x] pending[%s]", user_lw_device[i].name,
													k_work_busy_get(&user_lw_device[i].work),
													k_work_is_pending(&user_lw_device[i].work)?"true":"false");

	}


	/* 等待work执行完成 */
	shell_print(shell, "wq drain with work start");
	ret = k_work_queue_drain(&user_work_q, false);
	shell_print(shell, "wq drain end ret %d", ret);

	shell_print(shell, "wq drain without work start");
	ret = k_work_queue_drain(&user_work_q, false);
	shell_print(shell, "wq drain end ret %d", ret);


	/* 初始化work，用于耗时测试，执行函数为user_longwork_handler */
	for(int i = 0; i<MAX_WORK; i++){
		k_work_init(&user_lw_device[i].work, user_longwork_handler);
		sprintf(user_lw_device[i].name, "LWC%d", i);
		user_lw_device[i].shell = shell;
		shell_print(shell, "wq send device %s", user_lw_device[i].name);
		ret = k_work_submit_to_queue(&user_work_q, &user_lw_device[i]);
		shell_print(shell, "submit user_lw_device[%d] ret %d", i, ret);
	}

	struct k_work_sync sync;

	ret = k_work_cancel_sync(&user_lw_device[0].work, &sync);
	shell_print(shell, "k_work_cancel_sync %s ret %d", user_lw_device[0].name, ret);
	ret = k_work_cancel_sync(&user_lw_device[3].work, &sync);
	shell_print(shell, "k_work_cancel_sync %s ret %d", user_lw_device[3].name, ret);

	/* 检查work的状态 */
	for(int i = 0; i<MAX_WORK; i++){
		shell_print(shell, "%s busy[0x%x] pending[%s]", user_lw_device[i].name,
													k_work_busy_get(&user_lw_device[i].work),
													k_work_is_pending(&user_lw_device[i].work)?"true":"false");

	}

	/* Flush work 4 */
	shell_print(shell, "wq flush with work start");
	ret = k_work_flush(&user_lw_device[4].work, &sync);
	shell_print(shell, "wq flush end ret %d", ret);

	/* Flush work 3 */
	shell_print(shell, "wq flush without work start");
	ret = k_work_flush(&user_lw_device[3].work, &sync);
	shell_print(shell, "wq flush end ret %d", ret);

	return 0;
}

static int workq_system_workq(const struct shell *shell, size_t argc, char **argv)
{
	/* 初始化一个work，执行函数为user_work_handler */
	k_work_init(&system_device.work, user_work_handler);

	for(int i =0; i<MAX_WORK; i++){
		sprintf(system_device.name, "S%d", i);
		system_device.shell = shell;
		shell_print(shell, "wq send device %s", system_device.name);

		/* 发送work到workq，由workq执行work的函数 */
		k_work_submit(&system_device.work);
		uint32_t start = k_uptime_get_32();
		k_msleep(300);
		shell_print(shell, "msleep %d\n", k_uptime_get_32() - start);
	}
}


static int workq_delay_workq(const struct shell *shell, size_t argc, char **argv)
{
	int ret = 0;
	struct k_work_sync sync;

	/* 初始化一个delay workq */
	k_work_init_delayable(&delay_work, delay_work_handler);
	strcpy(delay_work.name, "UD");
	delay_work.shell = shell;

	/* 300ms后在user workqueue中执行该work */
	k_work_schedule_for_queue(&user_work_q, &delay_work.delay_work, K_MSEC(300));

	/* 该work存在，这次提交不会再执行, delay时间不变 */
	k_work_schedule_for_queue(&user_work_q, &delay_work.delay_work, K_MSEC(300));

	k_msleep(200);

	/* 200ms后检查状态， expires是指delay work到期的绝对时间， remain是指delay work还有多久到期 */
	shell_print(shell, "check 1 busy[0x%x] pengding[%s] expire %dms remain %dms",
											k_work_delayable_busy_get(&delay_work.delay_work),
											k_work_delayable_is_pending(&delay_work.delay_work)?"true":"false",
											k_ticks_to_ms_floor32(k_work_delayable_expires_get(&delay_work.delay_work)),
											k_ticks_to_ms_floor32(k_work_delayable_remaining_get(&delay_work.delay_work)));

	k_msleep(200);
	/* 200ms后检查状态 */
	shell_print(shell, "check 2 busy[0x%x] pengding[%s] expire %dms remain %dms",
											k_work_delayable_busy_get(&delay_work.delay_work),
											k_work_delayable_is_pending(&delay_work.delay_work)?"true":"false",
											k_ticks_to_ms_floor32(k_work_delayable_expires_get(&delay_work.delay_work)),
											k_ticks_to_ms_floor32(k_work_delayable_remaining_get(&delay_work.delay_work)));

	/* 300ms后在user workqueue中执行该work */
	k_work_schedule_for_queue(&user_work_q, &delay_work.delay_work, K_MSEC(300));
	k_msleep(200);

	/* 200ms后检查状态 */
	shell_print(shell, "check before busy[0x%x] pengding[%s] expire %dms remain %dms",
											k_work_delayable_busy_get(&delay_work.delay_work),
											k_work_delayable_is_pending(&delay_work.delay_work)?"true":"false",
											k_ticks_to_ms_floor32(k_work_delayable_expires_get(&delay_work.delay_work)),
											k_ticks_to_ms_floor32(k_work_delayable_remaining_get(&delay_work.delay_work)));

	/* 200ms 后在user workqueue中reschedule work, 改为3秒后执行 */
	k_work_reschedule_for_queue(&user_work_q, &delay_work.delay_work, K_MSEC(3000));
	shell_print(shell, "check change busy[0x%x] pengding[%s] expire %dms remain %dms",
											k_work_delayable_busy_get(&delay_work.delay_work),
											k_work_delayable_is_pending(&delay_work.delay_work)?"true":"false",
											k_ticks_to_ms_floor32(k_work_delayable_expires_get(&delay_work.delay_work)),
											k_ticks_to_ms_floor32(k_work_delayable_remaining_get(&delay_work.delay_work)));

	while(k_work_delayable_is_pending(&delay_work.delay_work)){
		k_msleep(10);
	}

	return 0;

}

static int workq_system_delay_workq(const struct shell *shell, size_t argc, char **argv)
{
	int ret = 0;
	struct k_work_sync sync;

	/* 初始化一个delay workq */
	k_work_init_delayable(&delay_work, delay_work_handler);
	strcpy(delay_work.name, "D");
	delay_work.shell = shell;

	/* 300ms后在系统workqueue中执行该work */
	k_work_schedule(&delay_work.delay_work, K_MSEC(300));

	/* 该work存在，这次提交不会再执行, delay时间不变 */
	k_work_schedule(&delay_work.delay_work, K_MSEC(3000));

	k_msleep(200);

	/* 200ms后检查状态， expires是指delay work到期的绝对时间， remain是指delay work还有多久到期 */
	shell_print(shell, "check 1 busy[0x%x] pengding[%s] expire %dms remain %dms",
											k_work_delayable_busy_get(&delay_work.delay_work),
											k_work_delayable_is_pending(&delay_work.delay_work)?"true":"false",
											k_ticks_to_ms_floor32(k_work_delayable_expires_get(&delay_work.delay_work)),
											k_ticks_to_ms_floor32(k_work_delayable_remaining_get(&delay_work.delay_work)));

	k_msleep(200);
	/* 200ms后检查状态 */
	shell_print(shell, "check 2 busy[0x%x] pengding[%s] expire %dms remain %dms",
											k_work_delayable_busy_get(&delay_work.delay_work),
											k_work_delayable_is_pending(&delay_work.delay_work)?"true":"false",
											k_ticks_to_ms_floor32(k_work_delayable_expires_get(&delay_work.delay_work)),
											k_ticks_to_ms_floor32(k_work_delayable_remaining_get(&delay_work.delay_work)));

	/* 300ms后在系统workqueue中执行该work */
	k_work_schedule(&delay_work.delay_work, K_MSEC(300));
	k_msleep(200);

	/* 200ms后检查状态 */
	shell_print(shell, "check before busy[0x%x] pengding[%s] expire %dms remain %dms",
											k_work_delayable_busy_get(&delay_work.delay_work),
											k_work_delayable_is_pending(&delay_work.delay_work)?"true":"false",
											k_ticks_to_ms_floor32(k_work_delayable_expires_get(&delay_work.delay_work)),
											k_ticks_to_ms_floor32(k_work_delayable_remaining_get(&delay_work.delay_work)));

	/* 200ms 后在系统workqueue中reschedule work, 改为3秒后执行 */
	k_work_reschedule(&delay_work.delay_work, K_MSEC(3000));
	shell_print(shell, "check change busy[0x%x] pengding[%s] expire %dms remain %dms",
											k_work_delayable_busy_get(&delay_work.delay_work),
											k_work_delayable_is_pending(&delay_work.delay_work)?"true":"false",
											k_ticks_to_ms_floor32(k_work_delayable_expires_get(&delay_work.delay_work)),
											k_ticks_to_ms_floor32(k_work_delayable_remaining_get(&delay_work.delay_work)));

	while(k_work_delayable_is_pending(&delay_work.delay_work)){
		k_msleep(10);
	}

	/* 30s后在系统workqueue中执行该work */
	k_work_schedule(&delay_work.delay_work, K_MSEC(30000));

	/* 做flush时，立即执行等待的delay work, 执行完成后才flush才会返回 */
	shell_print(shell, "wq flush with work start");
	ret = k_work_flush_delayable(&delay_work.delay_work, &sync);
	shell_print(shell, "wq flush with work end %d", ret);

	shell_print(shell, "wq flush without work start");
	ret = k_work_flush_delayable(&delay_work.delay_work, &sync);
	shell_print(shell, "wq flush with workout end %d", ret);


	/* 300ms后在系统workqueue中执行该work, 并提前cancel掉 */
	k_work_init_delayable(&delay_work, delay_longwork_handler);
	k_work_schedule(&delay_work.delay_work, K_MSEC(300));
	shell_print(shell, "wq cancel with workdelay start");
	ret = k_work_cancel_delayable(&delay_work.delay_work);
	shell_print(shell, "wq cancel with workdelay end %d", ret);
	shell_print(shell, "check cancel busy[0x%x] pengding[%s] expire %dms remain %dms",
											k_work_delayable_busy_get(&delay_work.delay_work),
											k_work_delayable_is_pending(&delay_work.delay_work)?"true":"false",
											k_ticks_to_ms_floor32(k_work_delayable_expires_get(&delay_work.delay_work)),
											k_ticks_to_ms_floor32(k_work_delayable_remaining_get(&delay_work.delay_work)));



	/* 立即在系统workqueue中执行该work, 在执行过程中canncel, 立即返回 */
	k_work_init_delayable(&delay_work, delay_longwork_handler);
	k_work_schedule(&delay_work.delay_work, K_NO_WAIT);
	k_msleep(200);
	shell_print(shell, "wq cancel with workdelay process do start");
	ret = k_work_cancel_delayable(&delay_work.delay_work);
	shell_print(shell, "wq cancel with workdelay process do end ret = %d", ret);
	shell_print(shell, "check cancel  busy[0x%x] pengding[%s] expire %dms remain %dms",
											k_work_delayable_busy_get(&delay_work.delay_work),
											k_work_delayable_is_pending(&delay_work.delay_work)?"true":"false",
											k_ticks_to_ms_floor32(k_work_delayable_expires_get(&delay_work.delay_work)),
											k_ticks_to_ms_floor32(k_work_delayable_remaining_get(&delay_work.delay_work)));

	while(k_work_delayable_is_pending(&delay_work.delay_work)){
		k_msleep(10);
	}

	/* 立即在系统workqueue中执行该work, 在执行过程中同步canncel , 要到执行完后才会返回*/
	k_work_init_delayable(&delay_work, delay_longwork_handler);
	k_work_schedule(&delay_work.delay_work, K_NO_WAIT);
	k_msleep(200);
	shell_print(shell, "wq sync cancel with workdelay process do start");
	ret = k_work_cancel_delayable_sync(&delay_work.delay_work, &sync);
	shell_print(shell, "wq sync cancel with workdelayprocess  do end ret = %d", ret);
	shell_print(shell, "check sync cancel sync busy[0x%x] pengding[%s] expire %dms remain %dms",
											k_work_delayable_busy_get(&delay_work.delay_work),
											k_work_delayable_is_pending(&delay_work.delay_work)?"true":"false",
											k_ticks_to_ms_floor32(k_work_delayable_expires_get(&delay_work.delay_work)),
											k_ticks_to_ms_floor32(k_work_delayable_remaining_get(&delay_work.delay_work)));

}

SHELL_STATIC_SUBCMD_SET_CREATE(sub_workq,
			       SHELL_CMD(user_wq, NULL, "user work queue", workq_user_workq),
				   SHELL_CMD(system_wq, NULL, "system work queue", workq_system_workq),
				   SHELL_CMD(user_delay_wq, NULL, "delay work", workq_delay_workq),
				   SHELL_CMD(system_delay_wq, NULL, "system delay work", workq_system_delay_workq),
			       SHELL_SUBCMD_SET_END
			       );

SHELL_CMD_REGISTER(workq, &sub_workq, "workq test commands", NULL);